#   Copyright (C) 2008      Tri Tech Information Systems Inc.
# 
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
# 
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
# 
#   You should have received a copy of the GNU General Public License along
#   with this program; if not, write to the Free Software Foundation, Inc.,
#   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#   
from pyxx.element import ElementWrapper
from formatters import generator, formatter, argument_list
from parser import CodeModel


class CallableWrapper( ElementWrapper ):


    def __init__(self, *args, **kwargs):
        ElementWrapper.__init__(self, *args, **kwargs)
        self.def_type = 'def'
        self.is_property = False

    @formatter( argument_list )
    def generate_argumentList( self ):
            return ( str(arg.type) for arg in self.item.arguments )
    
    @property
    def use_keywords(self):
        if self.is_property:
            return False
        return len(self.item.arguments) 

    @formatter( argument_list )
    def generate_keyargs( self ):
        model = self.item.model
        ns = model.globalNamespace
        
        idx = 0
        for arg in self.item.arguments:
            idx += 1
            name = arg.name
            if name is None:
                name = '__param%d' % idx

            if arg.defaultValue:
                type_obj = model.findItem(arg.type.qualifiedName, ns)
                default_string = arg.defaultValueExpression
                default_string = default_string.replace('\\', '\\\\')
                default_string = default_string.replace('"', '\\"')
                yield 'arg("%s") = defarg<%s>("%s", %s)' % (
                        name, str(arg.type), default_string, arg.defaultValueExpression)
            else:
                yield 'arg("%s")' % name

    @formatter( argument_list )
    def generate_params(self):
        args = zip( self.item.arguments, range(len(self.item.arguments)))
        return ( str(arg.type) + " v%d" % idx for arg, idx in args )

    @formatter( argument_list )
    def generate_pyparam_values(self):
        args = range(len(self.item.arguments))
        def arg_value( (arg, idx)):
            if arg.type.indirections:
                return "handle_ptr(v%d)" % idx
            if arg.type.isReference and not arg.type.isConstant:
                return "handle_ref(v%d)" % idx
            else:
                return "v%d" % idx


        return map(arg_value, zip(self.item.arguments, args))

    @formatter( argument_list )
    def generate_param_values(self):
        args = range(len(self.item.arguments))
        def arg_value( (arg, idx)):
           return "v%d" % idx


        return map(arg_value, zip(self.item.arguments, args))


    def allTypes(self):
        return set(arg.type for arg in self.item.arguments)

    def make_property(self):    
        """
        Transform this method into a property
        """
        self.def_type = "add_property"
        self.is_property = True


class BaseFunctionWrapper( CallableWrapper ):

    def __init__(self, *args, **kwargs):
        if 'wrapper_ctx' in kwargs:
            self.wrapper_ctx = kwargs['wrapper_ctx']
            del kwargs['wrapper_ctx']
        CallableWrapper.__init__(self, *args, **kwargs)

        self.callPolicy = ''
        self.returnType = self.resolveType(self.item.type)

        self.pick_default_policy()
        

    def allTypes(self):
        types = set()
        types.add( self.returnType )
        types.update( CallableWrapper.allTypes(self) )

        return types            

    def pick_default_policy(self):
        """
        This method decides which call policy is best to use by default
        """
        if self.returnType.indirections or self.returnType.isReference:
            # if attempting to return a pointer of reference
            # use the pointer registration system
            self.callPolicy = 'return_handled_ptr'

        if self.returnType.isReference and self.returnType.isConstant:
            # Returning by constant reference can be considered just as a return by value 
            self.callPolicy = 'return_value_policy<return_by_value>'


    def generate_functionExpr(self):
        return self.qualifiedName


    @generator
    @formatter( argument_list )
    def generate_defSpec( self ):
        return [ '"%(python_name)s"',
                self.function_pointer(),
                self.default_implementation(),
                self.full_call_policy(),
                self.docstring,
                self.use_keywords and '(%(keyargs)s)' or ''
                ]

    @generator
    def function_pointer(self):
        return "%(functionType)s&%(functionExpr)s"
        

    @generator
    def generate_functionType(self):
        if self.isStatic or not self.parent:
            return "(%(returnType)s (*)(%(argumentList)s))"
        else:
            return "(%(returnType)s (%(parentClass)s::*)(%(argumentList)s) %(maybeConst)s)"


    def default_implementation(self):
        return None

    @generator
    def full_call_policy(self):
        if not self.callPolicy:
            return ''
        else:
            return '%(callPolicy)s()'
        
        
    def generate_maybeConst(self):
        if self.isConstant:
            return 'const'
        else:
            return ''




class MethodWrapper( BaseFunctionWrapper ):

    def __init__( self, meth, klass, python_name = None ):
        BaseFunctionWrapper.__init__( self, meth, klass, python_name )

        if self.isAbstract:
            # Abstract methods automatically cause wrapped class to
            # specify no_init and noncopyable
            klass.noncopyable = True

                

    def generate_parentClass(self):
        if self.item.accessPolicy == CodeModel.AccessPolicy.Protected:
            return 'Wrap_' + self.parent.name
        else:
            return self.parent.qualifiedName


    @generator
    def generate_functionExpr(self):
        return '%(parentClass)s::%(name)s'
